package org.capybara.coffeehouse.insteon;

import org.apache.log4j.Logger;

public class InsteonMessage {

	private static Logger log = Logger.getLogger(InsteonMessage.class);
	
	public enum MessageLength {
		STANDARD(0x00), EXTENDED(0x10);

		private int flags;

		MessageLength(int flags) {
			this.flags = flags;
		}
		
		public int getFlags() {
			return flags;
		}
		
		public MessageLength valueOfFlag(int flags) {			
			for (MessageLength type : values()) {
				if (type.getFlags() == (flags & 0x10)) {
					return type;
				}
			}
			throw new IllegalArgumentException("no MessageLength for flags: "+Util.getHexByte(flags));
		}

	}
	
	public enum MessageType {
		BROADCAST(0x80), DIRECT(0x0), DIRECT_ACK(0x20), DIRECT_NAK(0xA0), GROUP_BROADCAST(0xC0), 
		GROUP_DIRECT_CLEANUP(0x40), GROUP_CLEANUP_ACK(0x60), GROUP_CLEANUP_NAK(0xE0);
		
		private int flags;

		MessageType(int flags) {
			this.flags = flags;
		}
		
		public int getFlags() {
			return flags;
		}
		
		public static MessageType valueOfFlag(int flags) {
			for (MessageType type : values()) {
				if (type.getFlags() == (flags & 0xE0)) {
					return type;
				}
			}
			throw new IllegalArgumentException("no MessageType for flags: "+Util.getHexByte(flags));
		}
	}
	
	private final InsteonAddress fromAddress;
	private final InsteonAddress toAddress;
	private final MessageLength messageLength;
	
	private final int[] userData = new int[14];
	private final int cmd1;
	private final int cmd2;

	
	private final MessageType typeFlags;
	
	private final int crc;
	
	public InsteonMessage(MessageLength type, InsteonAddress from, InsteonAddress to, int cmd1, int cmd2, MessageType flags, int[] userData) {
		this.messageLength = type;
		this.fromAddress = from;
		this.toAddress = to;
		this.typeFlags = flags;
		this.cmd1 = cmd1;
		this.cmd2 = cmd2;
		
		this.crc = 0;
		if (userData != null) {
			System.arraycopy(userData,0,this.userData,0,this.userData.length);
		}
	}
	
	public InsteonMessage(InsteonAddress to, int cmd1, int cmd2) {
		this.toAddress = to;
		this.fromAddress = PlmTest.plmAddress;
		this.cmd1 = cmd1;
		this.cmd2 = cmd2;
		this.typeFlags = MessageType.DIRECT;
		this.messageLength = MessageLength.STANDARD;
		this.crc = 0;
	}
	
	private int getFlags() {
		int flags = 0;
		flags |= typeFlags.getFlags();
		flags |= messageLength.getFlags();
		return flags;
	}
	
	public byte[] getPlmMessage() {
		byte[] message;
		if (messageLength == MessageLength.STANDARD) {
			message = new byte[6];
		} else {
			message = new byte[20];
		}
		
		System.arraycopy(getMessage(),3,message,0,message.length);
		
		return message;
	}
	
	public byte[] getMessage() {
		byte[] message;
		if (messageLength == MessageLength.STANDARD) {
			message = new byte[10];
		} else {
			message = new byte[24];
		}
		
		message[0] = (byte) fromAddress.getByte1();
		message[1] = (byte) fromAddress.getByte2();
		message[2] = (byte) fromAddress.getByte3();
		message[3] = (byte) toAddress.getByte1();
		message[4] = (byte) toAddress.getByte2();
		message[5] = (byte) toAddress.getByte3();
		message[6] = (byte) getFlags();
		message[7] = (byte) cmd1;
		message[8] = (byte) cmd2;
		message[9] = (byte) crc;
		
		if (messageLength == MessageLength.EXTENDED) {
			System.arraycopy(userData, 0, message, 9, userData.length);
			message[23] = (byte) crc;
		}
		
		return message;
	}
	
	public InsteonAddress getFromAddress() {
		return fromAddress;
	}

	public InsteonAddress getToAddress() {
		return toAddress;
	}

	public MessageLength getMessageType() {
		return messageLength;
	}

	public int[] getUserData() {
		return userData;
	}

	public int getCmd1() {
		return cmd1;
	}

	public int getCmd2() {
		return cmd2;
	}
	
	public String toString() {
		if (messageLength == MessageLength.STANDARD) {
			return "[STD from: "+fromAddress+" to: "+toAddress+" cmd1: "+Util.getHexByte(cmd1)+" cmd2: "+
				Util.getHexByte(cmd2)+" flags: "+typeFlags.name()+"]";
		}
		else if (messageLength == MessageLength.EXTENDED) {
			return "[EXT from: "+fromAddress+" to: "+toAddress+" cmd1: "+Util.getHexByte(cmd1)+" cmd2: "+
			Util.getHexByte(cmd2)+" flags: "+typeFlags.name()+" user data: "+Util.getHexString(userData)+"]";			
		} else {
			log.error("invalid message flags!");
			return "[INVALID]";
		}
	}
}
